home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / net_src.arc / ftpserv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-05-08  |  18.4 KB  |  754 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3. #define    LINELEN        128    /* Length of command buffer */
  4.  
  5. #include <stdio.h>
  6. #include <ctype.h>
  7. #include "global.h"
  8. #include "mbuf.h"
  9. #include "netuser.h"
  10. #include "timer.h"
  11. #include "tcp.h"
  12. #include "ftp.h"
  13. #include "telnet.h"
  14.  
  15. #ifdef UNIX
  16. #include "unix.h"
  17. #endif /* UNIX */
  18. #include "iface.h"
  19. #include "ax25.h"
  20. #include "lapb.h"
  21. #include "finger.h"
  22. #include "session.h"
  23. #include "nr4.h"
  24.  
  25. /* Command table */
  26. static char *commands[] = {
  27.     "user",
  28. #define    USER_CMD    0
  29.     "acct",
  30. #define    ACCT_CMD    1
  31.     "pass",
  32. #define    PASS_CMD    2
  33.     "type",
  34. #define    TYPE_CMD    3
  35.     "list",
  36. #define    LIST_CMD    4
  37.     "cwd",
  38. #define    CWD_CMD        5
  39.     "dele",
  40. #define    DELE_CMD    6
  41.     "name",
  42. #define    NAME_CMD    7
  43.     "quit",
  44. #define    QUIT_CMD    8
  45.     "retr",
  46. #define    RETR_CMD    9
  47.     "stor",
  48. #define    STOR_CMD    10
  49.     "port",
  50. #define    PORT_CMD    11
  51.     "nlst",
  52. #define    NLST_CMD    12
  53.     "pwd",
  54. #define    PWD_CMD        13
  55.     "xpwd",            /* For compatibility with 4.2BSD */
  56. #define    XPWD_CMD    14
  57.     "mkd ",
  58. #define    MKD_CMD        15
  59.     "xmkd",            /* For compatibility with 4.2BSD */
  60. #define    XMKD_CMD    16
  61.     "xrmd",            /* For compatibility with 4.2BSD */
  62. #define    XRMD_CMD    17
  63.     "rmd ",
  64. #define    RMD_CMD        18
  65.     "stru",
  66. #define    STRU_CMD    19
  67.     "mode",
  68. #define    MODE_CMD    20
  69.     NULLCHAR
  70. };
  71.  
  72. /* Response messages */
  73. static char banner[] = "220 %s FTP version %s ready at %s\r\n";
  74. static char badcmd[] = "500 Unknown command\r\n";
  75. static char unsupp[] = "500 Unsupported command or option\r\n";
  76. static char givepass[] = "331 Enter PASS command\r\n";
  77. static char logged[] = "230 Logged in\r\n";
  78. static char typeok[] = "200 Type OK\r\n";
  79. static char only8[] = "501 Only logical bytesize 8 supported\r\n";
  80. static char deleok[] = "250 File deleted\r\n";
  81. static char mkdok[] = "200 MKD ok\r\n";
  82. static char delefail[] = "550 Delete failed\r\n";
  83. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  84. static char badtype[] = "501 Unknown type \"%s\"\r\n";
  85. static char badport[] = "501 Bad port syntax\r\n";
  86. static char unimp[] = "502 Command not yet implemented\r\n";
  87. static char bye[] = "221 Goodbye!\r\n";
  88. static char nodir[] = "553 Can't read directory \"%s\"\r\n";
  89. static char cantopen[] = "550 Can't read file \"%s\"\r\n";
  90. static char sending[] = "150 Opening data connection for %s %s\r\n";
  91. static char cantmake[] = "553 Can't create \"%s\"\r\n";
  92. static char portok[] = "200 Port command okay\r\n";
  93. static char rxok[] = "226 File received OK\r\n";
  94. static char txok[] = "226 File sent OK\r\n";
  95. static char noperm[] = "550 Permission denied\r\n";
  96. static char noconn[] = "425 Data connection reset\r\n";
  97. static char notlog[] = "530 Please log in with USER and PASS\r\n";
  98. static char okay[] = "200 Ok\r\n";
  99.  
  100. static struct tcb *ftp_tcb;
  101.  
  102. /* Start up FTP service */
  103. ftp1(argc,argv)
  104. int argc;
  105. char *argv[];
  106. {
  107.     struct socket lsocket;
  108.     void ftpscr(),ftpscs();
  109.  
  110.     lsocket.address = ip_addr;
  111.     if(argc < 2)
  112.         lsocket.port = FTP_PORT;
  113.     else
  114.         lsocket.port = atoi(argv[1]);
  115.  
  116.     ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,ftpscr,NULLVFP,ftpscs,0,(char *)NULL);
  117. }
  118. /* Shut down FTP server */
  119. ftp0()
  120. {
  121.     if(ftp_tcb != NULLTCB)
  122.         close_tcp(ftp_tcb);
  123. }
  124. /* FTP Server Control channel State change upcall handler */
  125. static
  126. void
  127. ftpscs(tcb,old,new)
  128. struct tcb *tcb;
  129. char old,new;
  130. {
  131.     extern char hostname[],version[];
  132.     struct ftp *ftp,*ftp_create();
  133.     void ftp_delete();
  134.     char *inet_ntoa();
  135.     long t;
  136.     char *cp,*cp1;
  137.  
  138.     switch(new){
  139. /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  140.  * leaving it unset waits for the three-way handshake to complete before
  141.  * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  142.  * so its use is not (yet) recommended.
  143. */
  144. #ifdef    QUICKSTART
  145.     case SYN_RECEIVED:
  146. #else
  147.     case ESTABLISHED:
  148. #endif
  149.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  150.             /* No space, kill connection */
  151.             close_tcp(tcb);
  152.             return;
  153.         }
  154.         ftp->control = tcb;        /* Downward link */
  155.         tcb->user = (char *)ftp;    /* Upward link */
  156.  
  157.         /* Set default data port */
  158.         ftp->port.address = tcb->conn.remote.address;
  159.         ftp->port.port = FTPD_PORT;
  160.  
  161.         /* Note current directory */
  162.         log(tcb,"open FTP");
  163.         time(&t);
  164.         cp = ctime(&t);
  165.         if((cp1 = index(cp,'\n')) != NULLCHAR)
  166.             *cp1 = '\0';
  167.         tprintf(ftp->control,banner,hostname,version,cp);
  168.         break;        
  169.     case CLOSE_WAIT:
  170.         close_tcp(tcb);
  171.         break;
  172.     case CLOSED:
  173.         log(tcb,"close FTP");
  174.         if((ftp = (struct ftp *)tcb->user) != NULLFTP ||
  175.            !tcpval(ftp->control)) {   /* control session existing ? */
  176.             ftp_delete(ftp);
  177.         }
  178.         /* Check if server is being shut down */
  179.         if(tcb == ftp_tcb)
  180.             ftp_tcb = NULLTCB;
  181.         del_tcp(tcb);
  182.         break;
  183.     }
  184. }
  185.  
  186. /* FTP Server Control channel Receiver upcall handler */
  187. static
  188. void
  189. ftpscr(tcb,cnt)
  190. struct tcb *tcb;
  191. int16 cnt;
  192. {
  193.     register struct ftp *ftp;
  194.     char c;
  195.     struct mbuf *bp;
  196.     void ftpcommand();
  197.  
  198.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  199.         /* Unknown connection, just kill it */
  200.         close_tcp(tcb);
  201.         return;
  202.     }
  203.     switch(ftp->state){
  204.     case COMMAND_STATE:
  205.         /* Assemble an input line in the session buffer. Return if incomplete */
  206.         recv_tcp(tcb,&bp,0);
  207.         while(pullup(&bp,&c,1) == 1){
  208.             switch(c){
  209.             case '\r':    /* Strip cr's */
  210.                 continue;
  211.             case '\n':    /* Complete line; process it */
  212.                 ftp->buf[ftp->cnt] = '\0';
  213.                 ftpcommand(ftp);
  214.                 ftp->cnt = 0;
  215.                 break;
  216.             default:    /* Assemble line */
  217.                 if(ftp->cnt != LINELEN-1)
  218.                     ftp->buf[ftp->cnt++] = c;
  219.                 break;
  220.             }
  221.         }
  222.         /* else no linefeed present yet to terminate command */
  223.         break;
  224.     case SENDING_STATE:
  225.     case RECEIVING_STATE:
  226.         /* Leave commands pending on receive queue until
  227.          * present command is done
  228.          */
  229.         break;
  230.     }
  231. }
  232.  
  233. /* FTP server data channel connection state change upcall handler */
  234. void
  235. ftpsds(tcb,old,new)
  236. struct tcb *tcb;
  237. char old,new;
  238. {
  239.     register struct ftp *ftp;
  240.  
  241.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  242.         /* Unknown connection. Kill it */
  243.         del_tcp(tcb);
  244.     } else if((old == FINWAIT1 || old == CLOSING) && ftp->state == SENDING_STATE){
  245.         /* We've received an ack of our FIN while sending; we're done */
  246.         ftp->state = COMMAND_STATE;
  247.         tprintf(ftp->control,txok);
  248.         /* Kick command parser if something is waiting */
  249.         if(ftp->control->rcvcnt != 0)
  250.             ftpscr(ftp->control,ftp->control->rcvcnt);
  251.     } else if(ftp->state == RECEIVING_STATE && new == CLOSE_WAIT){
  252.         /* FIN received on incoming file */
  253. #ifdef    CPM
  254.         if(ftp->type == ASCII_TYPE)
  255.             putc(CTLZ,ftp->fp);
  256. #endif
  257.         close_tcp(tcb);
  258.         if(ftp->fp != stdout)
  259.             fclose(ftp->fp);
  260.         ftp->fp = NULLFILE;
  261.         ftp->state = COMMAND_STATE;
  262.         tprintf(ftp->control,rxok);
  263.         /* Kick command parser if something is waiting */
  264.         if(ftp->control->rcvcnt != 0)
  265.             ftpscr(ftp->control,ftp->control->rcvcnt);
  266.     } else if(new == CLOSED){
  267.         if(tcb->reason != NORMAL){
  268.             /* Data connection was reset, complain about it */
  269.             tprintf(ftp->control,noconn);
  270.             /* And clean up */
  271.             if(ftp->fp != NULLFILE && ftp->fp != stdout)
  272.                 fclose(ftp->fp);
  273.             ftp->fp = NULLFILE;
  274.             ftp->state = COMMAND_STATE;
  275.             /* Kick command parser if something is waiting */
  276.             if(ftp->control->rcvcnt != 0)
  277.                 ftpscr(ftp->control,ftp->control->rcvcnt);
  278.         }
  279.         /* Clear only if another transfer hasn't already started */
  280.         if(ftp->data == tcb)
  281.             ftp->data = NULLTCB;
  282.         del_tcp(tcb);
  283.     }
  284. }
  285.  
  286. /* Parse and execute ftp commands */
  287. static
  288. void
  289. ftpcommand(ftp)
  290. register struct ftp *ftp;
  291. {
  292.     void ftpdr(),ftpdt(),ftpsds();
  293.     char *cmd,*arg,*cp,**cmdp,*file;
  294.     char *pathname();
  295.     char *mode;
  296.     struct socket dport;
  297.     int i;
  298.  
  299. #ifndef    CPM
  300.     FILE *dir();
  301. #endif
  302.  
  303.     cmd = ftp->buf;
  304.     if(ftp->cnt == 0){
  305.         /* Can't be a legal FTP command */
  306.         tprintf(ftp->control,badcmd);
  307.         return;
  308.     }    
  309.     cmd = ftp->buf;
  310.  
  311. #ifdef    UNIX
  312.     /* Translate first word to lower case */
  313.     for(cp = cmd;*cp != ' ' && *cp != '\0';cp++)
  314.         *cp = tolower(*cp);
  315. #else
  316.     /* Translate entire buffer to lower case */
  317.     for(cp = cmd;*cp != '\0';cp++)
  318.         *cp = tolower(*cp);
  319. #endif
  320.     /* Find command in table; if not present, return syntax error */
  321.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  322.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  323.             break;
  324.     if(*cmdp == NULLCHAR){
  325.         tprintf(ftp->control,badcmd);
  326.         return;
  327.     }
  328.     /* Allow only USER, PASS and QUIT before logging in */
  329.     if(ftp->cd == NULLCHAR || ftp->path[0] == NULLCHAR){
  330.         switch(cmdp-commands){
  331.         case USER_CMD:
  332.         case PASS_CMD:
  333.         case QUIT_CMD:
  334.             break;
  335.         default:
  336.             tprintf(ftp->control,notlog);
  337.             return;
  338.         }
  339.     }
  340.     arg = &cmd[strlen(*cmdp)];
  341.     while(*arg == ' ')
  342.         arg++;
  343.  
  344.     /* Execute specific command */
  345.     switch(cmdp-commands){
  346.     case USER_CMD:
  347.         if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  348.             close_tcp(ftp->control);
  349.             break;
  350.         }
  351.         strcpy(ftp->username,arg);
  352.         tprintf(ftp->control,givepass);
  353.          /* erase all user info from possible previous session */
  354.          for(i = 0; i < MAXPATH; i++){
  355.              if(ftp->path[i] != NULLCHAR){
  356.                  free(ftp->path[i]);
  357.                  ftp->path[i] = NULLCHAR;
  358.              }
  359.              ftp->perms[i] = 0;
  360.          }
  361.          if(ftp->cd != NULLCHAR){
  362.              free(ftp->cd);
  363.              ftp->cd = NULLCHAR;
  364.          }
  365.         break;
  366.     case TYPE_CMD:
  367.         switch(arg[0]){
  368.         case 'A':
  369.         case 'a':    /* Ascii */
  370.             ftp->type = ASCII_TYPE;
  371.             tprintf(ftp->control,typeok);
  372.             break;
  373.         case 'l':
  374.         case 'L':
  375.             while(*arg != ' ' && *arg != '\0')
  376.                 arg++;
  377.             if(*arg == '\0' || *++arg != '8'){
  378.                 tprintf(ftp->control,only8);
  379.                 break;
  380.             }    /* Note fall-thru */
  381.         case 'B':
  382.         case 'b':    /* Binary */
  383.         case 'I':
  384.         case 'i':    /* Image */
  385.             ftp->type = IMAGE_TYPE;
  386.             tprintf(ftp->control,typeok);
  387.             break;
  388.         default:    /* Invalid */
  389.             tprintf(ftp->control,badtype,arg);
  390.             break;
  391.         }
  392.         break;
  393.     case QUIT_CMD:
  394.         tprintf(ftp->control,bye);
  395.         close_tcp(ftp->control);
  396.         break;
  397.     case RETR_CMD:
  398.         /* Disk operation; return ACK now */
  399.         tcp_output(ftp->control);
  400.         file = pathname(ftp->cd,arg);
  401.         if(ftp->type == IMAGE_TYPE)
  402.             mode = binmode[READ_BINARY];
  403.         else
  404.             mode = "r";
  405.         if(!permcheck(ftp,RETR_CMD,file)){
  406.              tprintf(ftp->control,noperm);
  407.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  408.             tprintf(ftp->control,cantopen,file);
  409.         } else {
  410.             log(ftp->control,"RETR %s",file);
  411.             dport.address = ip_addr;
  412.             dport.port = FTPD_PORT;
  413.             ftp->state = SENDING_STATE;
  414.             tprintf(ftp->control,sending,"RETR",arg);
  415.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  416.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  417.         }
  418.         free(file);
  419.         break;
  420.     case STOR_CMD:
  421.         /* Disk operation; return ACK now */
  422.         tcp_output(ftp->control);
  423.         file = pathname(ftp->cd,arg);
  424.         if(ftp->type == IMAGE_TYPE)
  425.             mode = binmode[WRITE_BINARY];
  426.         else
  427.             mode = "w";
  428.         if(!permcheck(ftp,STOR_CMD,file)){
  429.              tprintf(ftp->control,noperm);
  430.             free(file);
  431.              break;
  432.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  433.             tprintf(ftp->control,cantmake,file);
  434.         } else {
  435.             log(ftp->control,"STOR %s",file);
  436.             dport.address = ip_addr;
  437.             dport.port = FTPD_PORT;
  438.             ftp->state = RECEIVING_STATE;
  439.             tprintf(ftp->control,sending,"STOR",arg);
  440.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  441.              0,ftpdr,NULLVFP,ftpsds,ftp->control->tos,(char *)ftp);
  442.         }
  443.         free(file);
  444.         break;
  445.     case PORT_CMD:
  446.         if(pport(&ftp->port,arg) == -1){
  447.             tprintf(ftp->control,badport);
  448.         } else {
  449.             tprintf(ftp->control,portok);
  450.         }
  451.         break;
  452. #ifndef CPM
  453.     case LIST_CMD:
  454.         /* Disk operation; return ACK now */
  455.         tcp_output(ftp->control);
  456.  
  457.         file = pathname(ftp->cd,arg);
  458.         if(!permcheck(ftp,RETR_CMD,file)){
  459.              tprintf(ftp->control,noperm);
  460.         } else if((ftp->fp = dir(file,1)) == NULLFILE){
  461.             tprintf(ftp->control,nodir,file);
  462.         } else {
  463.             dport.address = ip_addr;
  464.             dport.port = FTPD_PORT;
  465.             ftp->state = SENDING_STATE;
  466.             tprintf(ftp->control,sending,"LIST",file);
  467.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  468.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  469.         }
  470.         free(file);
  471.         break;
  472.     case NLST_CMD:
  473.         /* Disk operation; return ACK now */
  474.         tcp_output(ftp->control);
  475.  
  476.         file = pathname(ftp->cd,arg);
  477.         if(!permcheck(ftp,RETR_CMD,file)){
  478.              tprintf(ftp->control,noperm);
  479.         } else if((ftp->fp = dir(file,0)) == NULLFILE){
  480.             tprintf(ftp->control,nodir,file);
  481.         } else {
  482.             dport.address = ip_addr;
  483.             dport.port = FTPD_PORT;
  484.             ftp->state = SENDING_STATE;
  485.             tprintf(ftp->control,sending,"NLST",file);
  486.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  487.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  488.         }
  489.         free(file);
  490.         break;
  491.     case CWD_CMD:
  492.         tcp_output(ftp->control);    /* Disk operation; return ACK now */
  493.  
  494.         file = pathname(ftp->cd,arg);
  495.         if(!permcheck(ftp,RETR_CMD,file)){
  496.              tprintf(ftp->control,noperm);
  497.             free(file);
  498. #if      (defined(MSDOS) || defined(ATARI_ST))
  499.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  500.         } else if(strcmp(file,"\\") == 0 || access(file,0) == 0){
  501. #else
  502.         } else if(access(file,0) == 0){    /* See if it exists */
  503. #endif
  504.             /* Succeeded, record in control block */
  505.             free(ftp->cd);
  506.             ftp->cd = file;
  507.             tprintf(ftp->control,pwdmsg,file);
  508.         } else {
  509.             /* Failed, don't change anything */
  510.             tprintf(ftp->control,nodir,file);
  511.             free(file);
  512.         }
  513.         break;
  514.     case XPWD_CMD:
  515.     case PWD_CMD:
  516.         tprintf(ftp->control,pwdmsg,ftp->cd);
  517.         break;
  518. #else
  519.     case LIST_CMD:
  520.     case NLST_CMD:
  521.     case CWD_CMD:
  522.     case XPWD_CMD:
  523.     case PWD_CMD:
  524. #endif
  525.     case ACCT_CMD:        
  526.         tprintf(ftp->control,unimp);
  527.         break;
  528.     case DELE_CMD:
  529.         file = pathname(ftp->cd,arg);
  530.         if(!permcheck(ftp,DELE_CMD,file)){
  531.              tprintf(ftp->control,noperm);
  532.         } else if(unlink(file) == 0){
  533.             tprintf(ftp->control,deleok);
  534.         } else {
  535.             tprintf(ftp->control,delefail);
  536.         }
  537.         free(file);
  538.         break;
  539.     case PASS_CMD:
  540.         tcp_output(ftp->control);    /* Send the ack now */
  541.         ftplogin(ftp,arg);            
  542.         break;
  543. #ifndef    CPM
  544.     case XMKD_CMD:
  545.     case MKD_CMD:
  546.         file = pathname(ftp->cd,arg);
  547.         if(!permcheck(ftp,MKD_CMD,file)){
  548.             tprintf(ftp->control,noperm);
  549.         } else if(mkdir(file,0777) == 0){
  550.             tprintf(ftp->control,mkdok);
  551.         } else {
  552.             tprintf(ftp->control,cantmake);
  553.         }
  554.         free(file);
  555.         break;
  556.     case XRMD_CMD:
  557.     case RMD_CMD:
  558.         file = pathname(ftp->cd,arg);
  559.         if(!permcheck(ftp,RMD_CMD,file)){
  560.              tprintf(ftp->control,noperm);
  561.         } else if(rmdir(file) == 0){
  562.             tprintf(ftp->control,deleok);
  563.         } else {
  564.             tprintf(ftp->control,delefail);
  565.         }
  566.         free(file);
  567.         break;
  568.     case STRU_CMD:
  569.         if(tolower(arg[0]) != 'f')
  570.             tprintf(ftp->control,unsupp);
  571.         else
  572.             tprintf(ftp->control,okay);
  573.         break;
  574.     case MODE_CMD:
  575.         if(tolower(arg[0]) != 's')
  576.             tprintf(ftp->control,unsupp);
  577.         else
  578.             tprintf(ftp->control,okay);
  579.         break;
  580.     }
  581. #endif
  582. }
  583. static
  584. int
  585. pport(sock,arg)
  586. struct socket *sock;
  587. char *arg;
  588. {
  589.     int32 n;
  590.     int atoi(),i;
  591.  
  592.     n = 0;
  593.     for(i=0;i<4;i++){
  594.         n = atoi(arg) + (n << 8);
  595.         if((arg = index(arg,',')) == NULLCHAR)
  596.             return -1;
  597.         arg++;
  598.     }
  599.     sock->address = n;
  600.     n = atoi(arg);
  601.     if((arg = index(arg,',')) == NULLCHAR)
  602.         return -1;
  603.     arg++;
  604.     n = atoi(arg) + (n << 8);
  605.     sock->port = n;
  606.     return 0;
  607. }
  608. /* Attempt to log in the user whose name is in ftp->username and password
  609.  * in pass
  610.  */
  611. static
  612. ftplogin(ftp,pass)
  613. struct ftp *ftp;
  614. char *pass;
  615. {
  616.     char buf[80],*cp,*cp1,*getnenv();
  617.     FILE *fp;
  618.     int anony = 0;
  619. #ifdef UNIX
  620.     char *p;
  621.     char userfn[256];
  622.     char *getenv();
  623. #endif /* UNIX */
  624.     int i;
  625.  
  626.     if((fp = fopen(userfile,"r")) == NULLFILE){
  627.         /* Userfile doesn't exist */
  628.         tprintf(ftp->control,noperm);
  629.         return;
  630.     }
  631.  
  632.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  633.         if(buf[0] == '#')
  634.             continue;    /* Comment */
  635.         if((cp = index(buf,' ')) == NULLCHAR)
  636.             /* Bogus entry */
  637.             continue;
  638.         *cp++ = '\0';        /* Now points to password */
  639.         if(strcmp(ftp->username,buf) == 0)
  640.             break;        /* Found user name */
  641.     }
  642.     if(feof(fp)){
  643.         /* User name not found in file */
  644.         fclose(fp);
  645.         tprintf(ftp->control,noperm);
  646.         return;
  647.     }
  648.     fclose(fp);
  649.     /* Look for space after password field in file */
  650.     if((cp1 = index(cp,' ')) == NULLCHAR){
  651.         /* Invalid file entry */
  652.         tprintf(ftp->control,noperm);
  653.         return;
  654.     }
  655.     *cp1++ = '\0';    /* Now points to first path field */
  656.     if(strcmp(cp,"*") == 0)
  657.         anony = 1;    /* User ID is password-free */
  658.     if(!anony && strcmp(cp,pass) != 0){
  659.         /* Password required, but wrong one given */
  660.         tprintf(ftp->control,noperm);
  661.         return;
  662.     }
  663.     for(i = 0; i< MAXPATH; i++){
  664.       if((cp = index(cp1,' ')) == NULLCHAR){
  665.         /* Permission field missing, assume end of line */
  666.         break;
  667.       }
  668.       *cp++ = '\0';    /* now points to permission field */
  669.       ftp->path[i] = malloc((unsigned)strlen(cp1)+1);
  670.       strcpy(ftp->path[i],cp1);
  671.       /* set the permission bits */
  672.       ftp->perms[i] = atoi(cp);
  673.       if ((cp1 = index(cp,' ')) == NULLCHAR){
  674.         /* no next path field, so assume end of line */
  675.         break;
  676.       }
  677.       *cp1++ = '\0'; /* cp1 now points to the next path field */
  678.     }
  679.  
  680.     /* Set up current directory and LAST specified path prefix */
  681.     for (i= MAXPATH - 1; i>=0; i--)
  682.       if (ftp->perms[i])
  683.         break;
  684.  
  685.     ftp->cd = malloc((unsigned)strlen(ftp->path[i])+1);
  686.     strcpy(ftp->cd,ftp->path[i]);
  687.     
  688.     tprintf(ftp->control,logged);
  689.     if(!anony)
  690.         log(ftp->control,"%s logged in",ftp->username);
  691.     else
  692.         log(ftp->control,"%s logged in, ID %s",ftp->username,pass);
  693. }        
  694.  
  695. #if    (defined(MSDOS) || defined(ATARI_ST))
  696. /* Illegal characters in a DOS filename */
  697. char badchars[] = "\"[]|<>+=;,";
  698. #endif
  699.  
  700. /* Return 1 if the file operation is allowed, 0 otherwise */
  701. permcheck(ftp,op,file)
  702. struct ftp *ftp;
  703. int op;
  704. char *file;
  705. {
  706.     char *cp;
  707.     int i;
  708.  
  709.     if(file == NULLCHAR || ftp->path[0] == NULLCHAR)
  710.         return 0;    /* Probably hasn't logged in yet */
  711. #if    (defined(MSDOS) || defined(ATARI_ST))
  712.     /* Check for characters illegal in MS-DOS file names */
  713.     for(cp = badchars;*cp != '\0';cp++){
  714.         if(index(file,*cp) != NULLCHAR)
  715.             return 0;    
  716.     }
  717. #endif
  718. #if    (!AMIGA && !MAC)
  719.     /* The target file must be under the user's allowed search path */
  720.      for(i = 0; i < MAXPATH; i++)
  721.          if(ftp->path[i] != NULLCHAR &&
  722.             strncmp(file,ftp->path[i],strlen(ftp->path[i])) == 0)
  723.              break;
  724.  
  725.     if(i == MAXPATH)
  726.         return 0;
  727. #endif
  728.  
  729.     switch(op){
  730.     case RETR_CMD:
  731.         /* User must have permission to read files */
  732.         if(ftp->perms[i] & FTP_READ)
  733.             return 1;
  734.         return 0;
  735.     case DELE_CMD:
  736.     case RMD_CMD:
  737.         /* User must have permission to (over)write files */
  738.         if(ftp->perms[i] & FTP_WRITE)
  739.             return 1;
  740.         return 0;
  741.     case STOR_CMD:
  742.     case MKD_CMD:
  743.         /* User must have permission to (over)write files, or permission
  744.          * to create them if the file doesn't already exist
  745.          */
  746.         if(ftp->perms[i] & FTP_WRITE)
  747.             return 1;
  748.         if(access(file,2) == -1 && (ftp->perms[i] & FTP_CREATE))
  749.             return 1;
  750.         return 0;
  751.     }
  752.     return 0;    /* "can't happen" -- keep lint happy */
  753. }
  754.